package top.healthylife.gateway.core.util;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import top.healthylife.basics.utils.exception.BusinessException;
import top.healthylife.basics.utils.utils.BeanValidator;
import top.healthylife.gateway.dto.annotation.ApiField;
import top.healthylife.gateway.dto.processor.GroupDefaultProcessor;
import top.healthylife.gateway.dto.processor.GroupProcessor;

import javax.validation.ConstraintViolation;
import javax.validation.groups.Default;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author maoxiaomeng
 */
@Slf4j
public class ParameterCheckUtils {

    /**
     * 属性值的分组处理类
     */
    protected static Map<Class<?>, GroupProcessor> classMap = new HashMap<>();
    /**
     * 字段的检查对象
     */
    protected static Map<Class<?>, FieldsCheck> fieldsCheckMap = new HashMap<>();

    /**
     * 默认的字段分组缓存对象
     *
     * @author maoxiaomeng
     */
    @Data
    @Accessors(chain = true)
    public static class FieldsCheck {
        List<Field> list;
        List<Field> groupList;
    }

    /**
     * 获取校验参数
     *
     * @param aClass 对象的属性值
     */
    public static FieldsCheck getDeclaredFields(Class<?> aClass) {
        if (!fieldsCheckMap.containsKey(aClass)) {
            scanClassDeclaredFields(aClass);
        }
        return fieldsCheckMap.get(aClass);
    }

    /**
     * 单个校验，并分组处理
     *
     * @param data 请求参数
     * @param list 所有的字段集对象
     */
    @SneakyThrows
    public static <ApiReqDto> void singleCheckException(ApiReqDto data, List<Field> list) {
        List<String> error = new ArrayList<>();
        Class<?> aClass = data.getClass();
        for (Field field : list) {
            field.setAccessible(true);
            List<String> collect = BeanValidator.getValidator().validateValue(aClass, field.getName(), field.get(data), Default.class)
                    .stream()
                    .map(ConstraintViolation::getMessage)
                    .filter(ObjectUtil::isNotNull)
                    .collect(Collectors.toList());
            error.addAll(collect);
        }
        if (ObjectUtil.isNotNull(error)) {
            log.error("参数校验不通过,{}", error);
            throw new BusinessException(JSONObject.toJSONString(error));
        }
    }

    /**
     * 扫描此Class的注解属性
     *
     * @param data 类的Class
     * @author maoxiaomeng
     */
    protected static void scanClassDeclaredFields(Class<?> data) {
        List<Field> list = new ArrayList<>();
        List<Field> groupList = new ArrayList<>();
        //找字段属性有这个注解的
        Arrays.stream(data.getDeclaredFields())
                .filter(e -> e.isAnnotationPresent(ApiField.class))
                .forEach(e -> {
                    if (e.getAnnotation(ApiField.class).group().equals(GroupDefaultProcessor.class)) {
                        list.add(e);
                    } else {
                        groupList.add(e);
                    }
                });
        fieldsCheckMap.put(data, new FieldsCheck().setGroupList(groupList).setList(list));
    }


    /**
     * 分组检查具体的项
     *
     * @param data      处理数据
     * @param groupList 分组的属性
     * @author maoxiaomeng
     */
    @SneakyThrows
    public static <ApiReqDto> void groupCheckException(ApiReqDto data, List<Field> groupList) {
        Map<? extends Class<? extends GroupProcessor>, List<Field>> fieldMap = groupList.stream()
                .collect(Collectors.groupingBy(e -> e.getAnnotation(ApiField.class).group()));
        //拿到字段和分组信息
        //进行分组数据校验
        for (Class<? extends GroupProcessor> aClass : fieldMap.keySet()) {
            if (!classMap.containsKey(aClass)) {
                GroupProcessor groupProcessor = aClass.newInstance();
                classMap.put(aClass, groupProcessor);
            }
            GroupProcessor groupProcessor = classMap.get(aClass);
            groupProcessor.groupVerify(data.getClass(), (top.healthylife.gateway.dto.request.req.ApiReqDto) data, fieldMap.get(aClass));
        }
    }

    @SneakyThrows
    public static <ApiReqDto> void checkRequiredParam(Class<?> aClass, ApiReqDto dto) {
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            Class<?> type = field.getType();
            if (ClassUtil.isBasicType(type) || type.getSimpleName().equals("String")) {
                field.setAccessible(true);
                ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
                if (annotation != null && annotation.required() && ObjectUtil.isEmpty(field.get(dto))) {
                    throw new BusinessException("参数[{}]为必传项", field.getName());
                }
                continue;
            }
            if (type.getPackage().getName().equals("top.healthylife")) {
                checkRequiredParam(type, dto);
            }
        }

    }
}
